package cn.tedu.justone.pack01;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ZZ：JustOne
 * VX：Andylau2020smile
 * DY：JustOne2018
 * ZT：《》
 */
public class Demo01_初识lock {
    public static void main(String[] args) {

        //常用方法一：lock() 和 unlock();
        //常用方法一：lock() 和 unlock();
        //常用方法一：lock() 和 unlock();
        //
        // 1,对于Lock接口而言，获取锁和释放锁都是显式的。不像synchronized那样是隐式的。
        //   synchronized执行完代码 或 执行过程中遇到异常都会自动释放锁，而Lock接口的
        //   加锁和释放锁都必须以代码的形式写出来，
        //   用lock()加锁，
        //   用unlock()释放锁。
        // 2,由于Lock即使遇到异常也不会释放锁，所以我们通常的做法是在try{}中操作同步资源，
        //   如果有必要就用 catch{} 块捕获异常，然后在 finally{} 中释放锁，以保证发生
        //   异常时锁一定被释放。
        // 3,如果我们的代码不规范导致跳过了unlock()语句，就会使得这个锁永远不能被释放了，
        //   其他线程也无法再获得这个锁，这也是 Lock 相比于 synchronized 的一个劣势。

        Table01 table = new Table01();

        new Thread(){
            @Override
            public void run() {
                while(Table01.mi>0){
                    table.eatMi();
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                while(Table01.mi>0){
                    table.eatMi();
                }
            }
        }.start();
    }
}

class Table01{
    static int mi = 10;
    Lock myLock = new ReentrantLock();
    public void eatMi(){
        myLock.lock();//---------------上锁
        try{
            if(mi>0){
                System.out.println("第" + mi + "颗米正在被吃掉");
                mi--;
                mySleep(10);
            }
        }finally {
            myLock.unlock();//---------释放锁
        }
    }


    //为了让代码更优雅，编写统一的休眠方法
    public static void mySleep(long m){
        try {
            Thread.sleep(m);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
